package com.seckill.search.service.impl;

import com.seckill.api.cache.CategoryCache;
import com.seckill.api.client.user.UserClient;
import com.seckill.common.constants.ErrorInfo;
import com.seckill.common.domain.dto.PageDTO;
import com.seckill.common.exceptions.CommonException;
import com.seckill.common.utils.*;
import com.seckill.search.constants.SearchErrorInfo;
import com.seckill.search.domain.po.Item;
import com.seckill.search.domain.query.ItemPageQuery;
import com.seckill.search.domain.vo.ItemVO;
import com.seckill.search.repository.ItemRepository;
import com.seckill.search.service.ISearchService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements ISearchService {

    @Autowired
    private RestHighLevelClient restClient;


    @Autowired
    private UserClient userClient;

    @Autowired
    private CategoryCache categoryCache;


    @Override
    public PageDTO<ItemVO> queryItemsForPortal(ItemPageQuery query) {
        // 1.搜索数据
        SearchResponse response = searchForResponse(query, ItemVO.EXCLUDE_FIELDS);
        // 2.解析响应
        PageDTO<Item> result = handleSearchResponse(response, query.getPageSize());
        // 3.处理VO
        List<Item> list = result.getList();
        if (CollUtils.isEmpty(list)) {
            return PageDTO.empty(result.getTotal(), result.getPages());
        }
        // 3.1转换VO
        List<ItemVO> vos = new ArrayList<>(list.size());
        for (Item c : list) {
            ItemVO vo = BeanUtils.toBean(c, ItemVO.class);
            vos.add(vo);
        }
        return new PageDTO<>(result.getTotal(), result.getPages(), vos);
    }

    @Override
    public List<Long> queryItemsIdByName(String keyword) {
        // 1.创建Request
        SearchRequest request = new SearchRequest(ItemRepository.INDEX_NAME);
        // 2.构建DSL
        request.source()
                .query(QueryBuilders.matchPhraseQuery(ItemRepository.DEFAULT_QUERY_NAME, keyword))
                .fetchSource(new String[]{"id"}, null);
        // 3.查询
        SearchResponse response;
        try {
            response = restClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new CommonException(SearchErrorInfo.QUERY_ITEM_ERROR, e);
        }
        // 4.解析
        SearchHits searchHits = response.getHits();
        // 4.1.获取hits
        SearchHit[] hits = searchHits.getHits();
        if (hits.length == 0) {
            return CollUtils.emptyList();
        }
        // 4.2.获取id
        return Arrays.stream(hits)
                .map(SearchHit::getId)
                .map(Long::valueOf)
                .collect(Collectors.toList());
    }


    private SearchResponse searchForResponse(ItemPageQuery query, String[] excludeFields) {
        // 1.创建Request
        SearchRequest request = new SearchRequest(ItemRepository.INDEX_NAME);
        // 2.构建DSL
        // 2.1.构建query
        buildBasicQuery(request, query);
        // 2.2.排序
        String sortBy = query.getSortBy();
        if (StringUtils.isNotBlank(sortBy)) {
            request.source().sort(sortBy, query.getIsAsc() ? SortOrder.ASC : SortOrder.DESC);
        }
        // 2.3.分页
        request.source().from(query.from()).size(query.getPageSize());
        // 2.4.高亮
        request.source().highlighter(new HighlightBuilder().field(ItemRepository.DEFAULT_QUERY_NAME));
        // 2.5.source处理
        request.source().fetchSource(null, excludeFields);
        // 3.发送请求
        SearchResponse response = null;
        try {
            response = restClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new CommonException(ErrorInfo.Msg.SERVER_INTER_ERROR, e);
        }
        return response;
    }

    private void buildBasicQuery(SearchRequest request, ItemPageQuery query) {
        // 1.准备bool查询
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 2.关键字搜索
        String keyword = query.getKeyword();
        if (StringUtils.isBlank(keyword)) {
            queryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
            queryBuilder.must(QueryBuilders.matchPhraseQuery(ItemRepository.DEFAULT_QUERY_NAME, keyword));
        }
        // 3.其它条件
        if (query.getFirstCateId() != null) {
            queryBuilder.filter(QueryBuilders.termQuery(ItemRepository.CATEGORY_ID_LV1, query.getFirstCateId()));
        }
        if (query.getSecondCateId() != null) {
            queryBuilder.filter(QueryBuilders.termQuery(ItemRepository.CATEGORY_ID_LV2, query.getSecondCateId()));
        }
        if (query.getThirdCateId() != null) {
            queryBuilder.filter(QueryBuilders.termQuery(ItemRepository.CATEGORY_ID_LV3, query.getThirdCateId()));
        }
        // 4.写入request
        request.source().query(queryBuilder);
    }

    private PageDTO<Item> handleSearchResponse(SearchResponse response, int pageSize) {
        SearchHits searchHits = response.getHits();
        // 1.总条数
        long total = searchHits.getTotalHits().value;
        // 2.总页数
        long totalPages = (total + pageSize - 1) / pageSize;
        // 3.获取命中的数据
        SearchHit[] hits = searchHits.getHits();
        if (hits.length <= 0) {
            return new PageDTO<>(total, totalPages, CollUtils.emptyList());
        }
        // 4.遍历
        List<Item> list = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            // 5.获取某一条source
            String jsonSource = hit.getSourceAsString();
            // 6.反序列化
            Item Item = JsonUtils.toBean(jsonSource, Item.class);
            // 7.处理高亮
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (CollUtils.isNotEmpty(highlightFields)) {
                // 7.1.获取高亮结果
                HighlightField field = highlightFields.get(ItemRepository.DEFAULT_QUERY_NAME);
                Object[] fragments = field.getFragments();
                String value = StringUtils.join(fragments);
                // 7.2.覆盖非高亮结果
                Item.setName(value);
            }
            list.add(Item);
        }
        return new PageDTO<>(total, totalPages, list);
    }
}
